<?php
/******************************************************************************\
* pjjTextBase                                  Version 1.1                     *
* Copyright 2006+ Przemyslaw Jerzy Jackowski   All Rights Reserved.            *
* E-Mail: ptb@pjj.pl                           Script License: GPL             *
* Created  07/24/2006                          Last Modified 11/22/2006        *
* Scripts Archive at:                          http://www.pjj.pl/pjjtextbase/  *
*******************************************************************************/
// This version have comments, so you may better see how it works.

ob_start();
if (file_exists(dirname(__FILE__) . '/ptb_ini.php')) {
  require_once dirname(__FILE__) . '/ptb_ini.php';
} else {
  // defining default values:
  define('PTB_DEFAULT_DB_LOCATION', 'G');
  define('PTB_PATH_DB', '/db');
  define('PTB_FILE_DB_PREFIX', 'db_');
  define('PTB_NEWLINE', "\n");
  define('PTB_DEFAULT_SECURITY', 1);
  define('PTB_SEC_STR', "<?php die('Access denied!');?>");
  define('PTB_SHOW_ERRORS', TRUE);
  define('PTB_BOM', FALSE);
  define('PIPE', '&pipe;');
}
// -----------------------------------------------------------------------------
// -----------------------------------------------------[ internal functions ]--
// -----------------------------------------------------------------------------

// -------------------------------------------------[ ptb_internal_error_msg ]--
// displays error message and dies, if it is fatal
// must be 1st function
function ptb_internal_error_msg($filename, $error)
{
  if (strcmp('4.3.0', phpversion()) > 0) {
    return;
  } else if (PTB_SHOW_ERRORS) {
    $debug_array = debug_backtrace();
    if ($error == 'wrongcondition3') {
      $debug_cnt = 3; // since it's being called from a sub-function
    } else {
      $debug_cnt = 2;
    }
    $file = file($debug_array[$debug_cnt - 1]['file']);
    $body = ' called from<br />&nbsp;&nbsp;&nbsp;<strong>' . $debug_array[$debug_cnt - 1]['function'] . '</strong><br />in file<br />&nbsp;&nbsp;&nbsp;<strong>' . $debug_array[$debug_cnt - 1]['file'] . '</strong><br />on line no.<br />&nbsp;&nbsp;&nbsp;<strong>' . $debug_array[$debug_cnt-1]['line'] . '</strong>: ' . trim($file[$debug_array[$debug_cnt-1]['line'] - 1]) . '<br />message:<br />&nbsp;&nbsp;&nbsp;';
    echo '<pre style="clear:both;margin:20px 50px;padding:20px;background-color:#ffc;">';
    switch ($error) {
    case 'nofile':
      echo '<u>Fatal error</u>', $body;
      die("file <strong>$filename</strong> does not exist!<br /><br />Exiting...");
    case 'cantwrite':
      echo '<u>Fatal error</u>', $body;
      die("can't write to file <strong>$filename</strong>!<br /><br />Exiting...");
    case 'cantdelete':
      echo '<u>Fatal error</u>', $body;
      die("can't delete <strong>$filename</strong>!<br /><br />Exiting...");
    case 'cantopen':
      echo '<u>Fatal error</u>', $body;
      die("can't open <strong>$filename</strong> for writing!<br /><br />Exiting...");
    case 'wrongcondition1':
      echo '<u>Fatal error</u>', $body;
      die("error in condition: <strong>single quote</strong> expected!<br /><br />Exiting...");
    break;
    case 'wrongcondition2':
      echo '<u>Fatal error</u>', $body;
      die("error in condition: <strong>equal sign</strong> expected!<br /><br />Exiting...");
    break;
    case 'wrongcondition3':
      echo '<u>Fatal error</u>', $body;
      die("error in condition: <strong>double equal sign</strong> expected!<br /><br />Exiting...");
    break;
    case 'wrongcondition4':
      echo '<u>Fatal error</u>', $body;
      die("error in condition: <strong>double equal sign</strong> expected!<br /><br />Exiting...");
    break;
    }
    echo '</pre>';
  }
}
// -----------------------------------------------------[ ptb_internal_write ]--
// writes $data to $filename; $data can either be string (full record line) or array
// $header is 3-char long string: from scratch-secure-field names
function ptb_internal_write($filename, $location, $header, $data)
{
  $filename = ptb_internal_set_filename($filename, $location);
  $record = '';
  // first check if the file exists AND is writable
  if (file_exists($filename) AND !is_writable($filename)) {
    ptb_internal_error_msg($filename, 'cantopen');
  }
  // HEADER
  // UTF-8 signature?
  if ((PTB_BOM == TRUE) AND ($header[0] == 1)) {
    $strBuffer = chr(hexdec('ef')) . chr(hexdec('bb')) . chr(hexdec('bf'));
  } else {
    $strBuffer = '';
  }
  if ($header[1] == 1) {
    $strBuffer .= PTB_SEC_STR;
  }
  // if ($header[2] == 1) write file header, ie. line with field names:
  if ($header[2] == 1) {
    $keys = array_keys($data[0]);
    $fieldNamesLine = implode('|', $keys);
    if ($header[1] == 1) {
      $strBuffer .= PTB_NEWLINE;
    }
    $strBuffer .= $fieldNamesLine;
  }
  // DATA
  for ($i = 0, $c = count($data); $i < $c; $i++) {
    if (is_array($data)) {
      $record = implode('|', $data[$i]);
    } else {
      $record = $data;
    }
    $strBuffer .= PTB_NEWLINE . $record;
  }
  ignore_user_abort(true);
  // "ab" or "wb" below depends on $header[0]
  if ($header[0] == 1) {
    if (!($fp = fopen($filename, "wb"))) {
      ptb_internal_error_msg($filename, 'cantopen');
    }
  } else {
  if (!($fp = fopen($filename, "ab"))) {
      ptb_internal_error_msg($filename, 'cantopen');
    }
  }
  flock($fp, LOCK_EX);
  if (!fwrite($fp, $strBuffer)) {
    ptb_internal_error_msg($filename, 'cantwrite');
  }
  flock($fp, LOCK_UN);
  fclose($fp);
  ignore_user_abort(false);
}
// ----------------------------------------------------[ ptb_internal_update ]--
// updates given fields with given values in one record of $database
function ptb_internal_update($record, $record_number, $update)
{
  for ($i = 0, $c = count($update); $i < $c; $i++) {
    $record[$update[$i][0]] = $update[$i][1];
  }
  return($record);
}
// ----------------------------------------------[ ptb_internal_set_filename ]--
// points to $filename
function ptb_internal_set_filename($filename, $location)
{
/*
  if $filename exists, open it;
  $location = 'L'          : local (current) directory
  $location = 'G'          : global (universal) directory, set in PTB_PATH_DB
  $location = 'F'          : just filename (for absolute or relative paths with
                             no DOCUMENT_ROOT dependency)
  $location = '/directory' : specified directory
*/
  switch ($location) {
  case 'L':
    $filename = dirname(realpath($_SERVER['DOCUMENT_ROOT'] . $_SERVER['PHP_SELF'])) . DIRECTORY_SEPARATOR . $filename;
    break;
  case 'G':
    $filename = $_SERVER['DOCUMENT_ROOT'] . PTB_PATH_DB . DIRECTORY_SEPARATOR . $filename;
    break;
  case 'F':
    $filename = $filename;
    break;
  default:
    $filename = $_SERVER['DOCUMENT_ROOT'] . $location . DIRECTORY_SEPARATOR . $filename;
  }
  return($filename);
}
// -----------------------------------------------[ ptb_internal_isFieldName ]--
// finds if there's $fieldName in the $database
function ptb_internal_isFieldName($database, $fieldName)
{
  $keys = array_keys($database[0]);
  for ($i = 0, $c = ptb_count($keys); $i < $c; $i++) {
    if ($keys[$i] == $fieldName) {
      return TRUE;
    }
  }
  return FALSE;
}
// ---------------------------------------------[ ptb_internal_set_condition ]--
// prepares--for further eval()--$condition with values from $database
function ptb_internal_set_condition($database, $condition)
{
  if ((substr_count($condition, '=') == '1') AND (substr_count($condition, '!') == '0')) {
    ptb_internal_error_msg('', 'wrongcondition3');
  }
  // find field names:
  $fieldNames = array_keys($database[0]);
  // surround condition with brackets, so you needn't use them while invoking function
  $condition = '(' . $condition . ')';
  // change field names in loop into corresponding references to field values
  for ($j = 0, $c = count($fieldNames); $j < $c; $j++) {
    $condition = str_replace('\'' . $fieldNames[$j] . '\'', '$database[$i][\'' . $fieldNames[$j] . '\']', $condition);
  }
  return $condition;
}
// ---------------------------------------------[ ptb_internal_columns_equal ]--
// finds if two columns are equal (both keys and values); does not check order, though
function ptb_internal_columns_equal($database1, $field1, $database2, $field2)
{
  foreach ($database1 as $value) {
    $column1[][$field1] = $value[$field1];
  }
  foreach ($database2 as $value) {
    $column2[][$field2] = $value[$field2];
  }
  $commonValuesInBothColumns = array_intersect($column1, $column2);
  if (ptb_count($column1) == ptb_count($commonValuesInBothColumns)) {
    return TRUE;
  } else {
    return FALSE;
  }
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------[ external functions ]--
// -----------------------------------------------------------------------------

// ------------------------------------------------------------[ ptb_connect ]--
// reads database file to array
function ptb_connect($filename, $location = PTB_DEFAULT_DB_LOCATION, $recursive = TRUE)
{
  // set filename with full path info:
  $filename = ptb_internal_set_filename($filename, $location);
  if (!file_exists($filename)) {
    ptb_internal_error_msg($filename, 'nofile');
  }
  // read $filename as table of rows; $f_database means *whole* file
  // ie. array of lines, header included:
  $f_database = file($filename);
  // BOM
  if ((dechex(ord($f_database[0][0])) == 'ef') AND (dechex(ord($f_database[0][1])) == 'bb') AND (dechex(ord($f_database[0][2])) == 'bf')) {
    $f_database[0] = substr($f_database[0], 3);
  }
  $secure = (rtrim($f_database[0]) == PTB_SEC_STR) ? 1 : 0;
  // if database is empty, finish--as there's nothing to do:
  if ((!$secure AND count($f_database) == 1) OR ($secure AND count($f_database) == 2)) {
    return;
  }
  // find field names:
  $fieldNames = explode('|', rtrim($f_database[$secure]));
  $fieldNamesCount = count($fieldNames);

  $linkFields = array(); $j = 0;
  // check if there are any linking fields:
  for ($i = 0; $i < $fieldNamesCount; $i++) {
    if (($fieldNames[$i][0] == '@') OR ($fieldNames[$i][0] == '%')) {
      $linkFields[$j]['type'] = substr($fieldNames[$i], 0, 1);
      $linkFields[$j]['name'] = substr($fieldNames[$i], 1, strlen($fieldNames[$i]));
      $j++;
    }
  } // --> now $linkFields contains names of files to be linked
  // record number for consecutive indices; this is needed if there are comments
  // or empty lines in the datafile
  $recordNo = 0;
  // read lines, split them and populate into database:
  for ($i = 1 + $secure, $c = count($f_database); $i < $c; $i++) {
    // do not read empty lines nor comments!
    if (($f_database[$i] <> PTB_NEWLINE) AND ($f_database[$i][0] <> '#')) {
      $splitLine = explode('|', rtrim($f_database[$i]));
      for ($j = 0; $j < $fieldNamesCount; $j++) {
        // $database[$recordNo][$fieldNames[$j]] = str_replace(PIPE, '|', $splitLine[$j]);
        // line above has replacing &pipe;-->| switched on
        // line below has it switched off for higher speed
        $database[$recordNo][$fieldNames[$j]] = $splitLine[$j];
      }
      $recordNo++;
    }
  }
  // if ($recursive == false) do not link linked files --> end up
  if (!$recursive) {return $database;}

  // if there are any linked files then link them recursively:
  $cn_record = count($database);
  for ($i = 0, $c = count($linkFields); $i < $c; $i++) {
    // find extension for linked files (same as main file):
    $ext = explode('.', $filename);
    if (count($ext) > 1) {
      $ext = $ext[count($ext)-1];
    } else {
      $ext = '';
    }
    $linkedTable = ptb_connect(PTB_FILE_DB_PREFIX . $linkFields[$i]['name'] . '.' . $ext, $location);
    $linkedTableFields = array_keys($linkedTable[0]);
    $ccc = count($linkedTableFields);
    $cccc = count($linkedTable);
    if ($linkFields[$i]['type'] == '@') {
      // in loops:
      // 1. by number of records of basic database
      // 2. by number of fields of linked database add new fields to $database
      //

      // 1
      for ($j = 0; $j < $cn_record; $j++) {
      // 2 (single record)
        for ($k = 1; $k < $ccc; $k++) {
          // loop by number of records of linked file, ie. by number of unique ids:
          for ($m = 0; $m < $cccc; $m++) {
            // must be emptied:
            $linked_file_field_value = '';
            if ($linkedTable[$m]['id'] == $database[$j]['@' . $linkedTableFields[1]]) {
              // ...insert value of corresponding field:
              $linked_file_field_value = $linkedTable[$m][$linkedTableFields[$k]];
              // stop looping after finding id value
              break;
            }
          }
          // value of subsequent fields of $linkedTable corresponding with id value:
          $database[$j][$linkedTableFields[$k]] = $linked_file_field_value;
        }
      }
    } else {
      // for every multiField:
      for ($j = 0; $j < $cn_record; $j++) {
        // = for all basic table records
        if ($database[$j]['%' . $linkFields[$i]['name']] <> '') {
          $multiFieldValue = explode(',', $database[$j]['%' . $linkFields[$i]['name']]);
          for ($k = 0, $cc = count($multiFieldValue); $k < $cc; $k++) {
            // find corresponding value in linkedTable:
            for ($m = 0; $m < $cccc; $m++) {
              if ($multiFieldValue[$k] == $linkedTable[$m]['id']) {
                $linked_file_field_value = $linkedTable[$m][$linkFields[$i]['name']];
                break;
              }
            }
            if (!empty($linked_file_field_value)) {
              $database[$j][$linkFields[$i]['name']][$k] = $linked_file_field_value;
            }
          }
        } else {
          $database[$j][$linkFields[$i]['name']] = '';
        }
      }
    }
  }
  // return $database as table[record number][field name]
  return $database;
}
// --------------------------------------------------------------[ ptb_count ]--
// counts records in the $database
function ptb_count($database, $condition = '')
{
  if (is_array($database)) {
    if (empty($condition)) {
      return count($database);
    } else {
      $j = 0;
      $condition = ptb_internal_set_condition($database, $condition);
      for ($i = 0, $c = count($database); $i < $c; $i++) {
        eval("if ($condition) {\$j++;}");
      }
      return $j;
    }
  } else {
    return 0;
  }
}
// -------------------------------------------------------------[ ptb_create ]--
// creates empty datafile
function ptb_create($filename, $location, $fieldNamesLine)
{
  ptb_internal_write($filename, $location, '1'.PTB_DEFAULT_SECURITY.'0', $fieldNamesLine);
}
// --------------------------------------------------------------[ ptb_write ]--
// writes down $table to the datafile $filename
function ptb_write($filename, $location, $table, $security = PTB_DEFAULT_SECURITY)
{
  ptb_internal_write($filename, $location, '1'.$security.'1', $table);
}
// ----------------------------------------------------------------[ ptb_add ]--
// adds one record to the datafile
function ptb_add($filename, $location = PTB_DEFAULT_DB_LOCATION, $fieldValues)
{
  if (!file_exists(ptb_internal_set_filename($filename, $location))) {
    ptb_internal_error_msg(ptb_internal_set_filename($filename, $location), 'nofile');
  }
  ptb_internal_write($filename, $location, '000', $fieldValues);
}
// ---------------------------------------------------------------[ ptb_sort ]--
// sorts $table according to field(s) set by $sort
function ptb_sort($table, $sort) {
  // $sort eg. "field1, ASC, field2, DESC, field3, ASC"
  // OR "field1 ASC, field2 DESC, field3 ASC"
  $sort = preg_replace('/(\s*,\s*)|\s+/', ';', $sort);
  $sort = explode(';', $sort);
  // $sort should be even; if not, add ASC at the end:
  if (count($sort) & 1) {
    $sort[count($sort)] = 'ASC';
  }
  for ($i = 0, $c = count($sort); $i < $c; $i++) {
    eval("foreach (\$table as \$key => \$row) {\$$sort[$i][\$key] = \$row['$sort[$i]'];}");
    $i++;
  }
  $sortby = '';
  for ($i = 0, $c = count($sort)/2; $i < $c; $i++) {
    $sortby .= '$' . $sort[($i*2)] . ', SORT_' . $sort[($i*2 + 1)] . ',';
  }
  eval("array_multisort($sortby \$table);");
  return $table;
}
// ------------------------------------------------------------[ ptb_select ]--
// selects records that match given condition
function ptb_select($database, $condition, $sort = '', $limit = '')
{
  if (ptb_count($database) == 0) return 0;
  $condition = ptb_internal_set_condition($database, $condition);
  $result = '';
  $numArgs = func_get_args();
  if ((count($numArgs) == 3) AND (is_int($numArgs[2]))) {
    $limit = $numArgs[2];
    $sort = '';
  }
  if ((!isset($limit)) OR (!is_int($limit)) OR ($limit < 1) OR ($limit > ptb_count($database))) {
    $limit = ptb_count($database);
  }
  $j = 0;
  // check all records if they follow $condition, and if so -- write them to
  // $result table till amount of $limit is reached
  for ($i = 0, $c = ptb_count($database); $i < $c AND $j < $limit; $i++) {
    eval("if ($condition) {\$result[\$j] = \$database[\$i]; \$j++;}");
  }
  // if fields and sorting flags are given, do the sorting
  if ($sort != '') {
    if (is_array($result)) {
      $result = ptb_sort($result, $sort);
    }
  }
  return $result;
}
// -------------------------------------------------------------[ ptb_delete ]--
// deletes selected records from file
function ptb_delete($filename, $location, $condition)
{
  // in connect below $recursive must be FALSE, otherwise values of linked fields
  // would be added to returned table instead of just codes
  $database = ptb_connect($filename, $location, FALSE);
  if (ptb_count($database) == 0) return 0;
  $condition = ptb_internal_set_condition($database, $condition);
  $j = 0;
  $result = array();
  // check all records if they follow $condition; if NOT--add them to $result table
  for ($i = 0, $c = count($database); $i < $c; $i++) {
    eval("if (!$condition) {\$result[\$j] = \$database[\$i]; \$j++;}");
  }
  // if $condition is followed at least once...
  if (count($result) != count($database)) {
    if (count($result) != 0) {
      ptb_internal_write($filename, $location, '1'.PTB_DEFAULT_SECURITY.'1', $result);
    } else {
      ptb_internal_write($filename, $location, '1'.PTB_DEFAULT_SECURITY.'0', implode('|', array_keys($database[0])));
    }
  }
}
// -------------------------------------------------------------[ ptb_update ]--
// updates selected records in file
function ptb_update($filename, $location, $condition, $update)
{
  $database = ptb_connect($filename, $location, FALSE);
  if (ptb_count($database) == 0) return 0;
  $condition = ptb_internal_set_condition($database, $condition);
  // check all records if they follow $condition:
  // if they do    --> update adequate fields and save them in $result
  // if they don't --> copy them to $result
  // for performance reason first find fields and values to be updated,
  // so not to do it in loop;
  // $update means table [field, value] to be inserted as update
  $i = 0; $j = 0;
  while (strlen($update) > 0) {
    $update = ltrim($update);
    if ($update[0] <> '\'') {
      ptb_internal_error_msg('', 'wrongcondition1');
    } else {
      $update = substr($update, 1);
    }
    $pos = strpos($update, '\'');
    $result[$i][$j] = substr($update, 0, $pos);
    $update = ltrim(substr($update, $pos + 1));
    if ($update[0] <> '=') {
      ptb_internal_error_msg('', 'wrongcondition2');
    } else {
      $update = ltrim(substr($update, 1));
    }
    if ($update[0] == '\'') {
      $update = substr($update, 1);
      $pos = strpos($update, '\'');
      $result[$i][$j+1] = substr($update, 0, $pos);
      $update = ltrim(substr($update, $pos + 1));
    } else {
      $pos = strpos($update, ',');
      $result[$i][$j+1] = substr($update, 0, $pos);
      $update = ltrim(substr($update, $pos));
    }
    $update = substr($update, 1);
    $i++;
    $j = $j + 2;
  }
  // now do the update:
  $changeCounter = 0;
  for ($i = 0, $c = count($database); $i < $c; $i++) {
    eval("if ($condition) {\$database[\$i] = ptb_internal_update(\$database[\$i], \$i, \$result); \$changeCounter++;}");
  }
  // write file only if there were any updates
  if ($changeCounter > 0) {
    $f_database = file(ptb_internal_set_filename($filename, $location));
    $secure = (rtrim($f_database[0]) == PTB_SEC_STR) ? 1 : 0;
    ptb_internal_write($filename, $location, '1'.PTB_DEFAULT_SECURITY.'1', $database);
  }
}
// ----------------------------------------------------------------[ ptb_map ]--
// finds $field value while $equation "'fieldname' == 'value'" is met for this field
function ptb_map($database, $equation, $otherFieldName)
{
  $equation = explode('==', $equation);
  if (count($equation) != 2) {
    ptb_internal_error_msg('', 'wrongcondition4');
  }
  // with these two additional variables condition in loop below is computed faster!
  $tmp0 = trim($equation[0], " '");
  $tmp1 = trim($equation[1], " '");
  for ($i = 0, $c = ptb_count($database); $i < $c; $i++) {
    if ($database[$i][$tmp0] == $tmp1) {
      return($database[$i][$otherFieldName]);
    }
  }
}
// ---------------------------------------------------------[ ptb_listUnique ]--
// lists all unique values of $fieldname in $database
function ptb_listUnique($database, $fieldName)
{
  if (ptb_count($database) == 0) {return 0;}
  $uniques = array();
  $k = 0;
  for ($i = 0, $c = ptb_count($database); $i < $c; $i++) {
    $isAlready = 0;
    for ($j = 0, $cc = count($uniques); $j < $cc; $j++) {
      if ($database[$i][$fieldName] == $uniques[$j]) {
        $isAlready = 1;
        break;
      }
    }
    if ($isAlready == 0) {
      $uniques[$k] = $database[$i][$fieldName];
      $k++;
    }
  }
  sort($uniques);
  return($uniques);
}
// ----------------------------------------------------------------[ ptb_max ]--
// finds $field with the biggest value in $database
// not only numeric! sorts strings and displays the last
function ptb_max($database, $fieldname)
{
  if (ptb_count($database) == 0) return 0;
  $max = $database[0][$fieldname];
  for ($i = 1, $c = ptb_count($database); $i < $c; $i++) {
    if (($database[$i][$fieldname]) > $max) {
      $max = $database[$i][$fieldname];
    }
  }
  return($max);
}
// ----------------------------------------------------------------[ ptb_min ]--
// finds $field with the smallest value in $database
// not only numeric! sorts strings and displays the first
function ptb_min($database, $fieldname)
{
  if (ptb_count($database) == 0) return 0;
  $min = $database[0][$fieldname];
  for ($i = 1, $c = ptb_count($database); $i < $c; $i++) {
    if (($database[$i][$fieldname] <> '') AND ($database[$i][$fieldname] < $min)) {
      $min = $database[$i][$fieldname];
    }
  }
  return($min);
}
// --------------------------------------------------------------[ ptb_merge ]--
// juxtaposes columns from other database
function ptb_merge($database, $filename, $location = PTB_DEFAULT_DB_LOCATION)
{
  $database2 = ptb_connect($filename, $location);
  $commonKeys = array_intersect(array_keys($database[0]), array_keys($database2[0]));
  if (ptb_count($commonKeys) == 0) return $database;
  if (ptb_internal_columns_equal($database, $commonKeys[0], $database2, $commonKeys[0])) {
    $diffKeys = array_values(array_diff(array_keys($database2[0]), $commonKeys));
    for ($i = 0, $c = ptb_count($diffKeys); $i < $c; $i++) {
      for ($j = 0, $cc = ptb_count($database); $j < $cc; $j++) {
        $database[$j][$diffKeys[$i]] = $database2[$j][$diffKeys[$i]];
      }
    }
  }
  return $database;
}
// -------------------------------------------------------------[ ptb_append ]--
// appends table from file at the end of existing database
function ptb_append($database, $filename, $location = PTB_DEFAULT_DB_LOCATION, $recursive = TRUE)
{
  $keys = array_keys($database[0]);
  $database2 = ptb_connect($filename, $location, $recursive);
  $keys2 = array_keys($database2[0]);
  // first key must be 'id'
  if (($keys[0] <> 'id') OR ($keys2[0] <> 'id')) {
  } else {
    $maxId = ptb_max($database, 'id');
    for ($i = 0, $c = ptb_count($database2); $i < $c; $i++) {
      $database2[$i]['id'] = $maxId + $i + 1;
    }
  }
  return array_merge($database, $database2);
}
// ----------------------------------------------------[ ptb_changeFieldName ]--
function ptb_changeFieldName($filename, $location, $oldFieldName, $newFieldName)
{
  $table = ptb_connect($filename, $location, FALSE);
  $fieldNames = array_keys($table[0]);
  $existsOld = FALSE;
  for ($i = 0, $c = ptb_count($fieldNames); $i < $c; $i++) {
    if ($fieldNames[$i] == $oldFieldName) {
      $existsOld = TRUE;
      break;
    }
  }
  $uniqueNew = TRUE;
  for ($i = 0, $c = ptb_count($fieldNames); $i < $c; $i++) {
    if ($fieldNames[$i] == $newFieldName) {
      $uniqueNew = FALSE;
      break;
    }
  }
  if ($existsOld AND $uniqueNew) {
    for ($i = 0, $c = ptb_count($fieldNames); $i < $c; $i++) {
      if ($fieldNames[$i] == $oldFieldName) {
        $fieldNames[$i] = $newFieldName;
        break;
      }
    }
    ptb_internal_write($filename, $location, '1'.PTB_DEFAULT_SECURITY.'0', implode('|', $fieldNames));
    ptb_internal_write($filename, $location, '000', $table);
  }
}
// -----------------------------------------------------------[ ptb_addField ]--
function ptb_addField($filename, $location, $newFieldName, $defaultValue = '')
{
  $table = ptb_connect($filename, $location, FALSE);
  $fieldNames = array_keys($table[0]);
  $existsFieldName = FALSE;
  for ($i = 0, $c = ptb_count($fieldNames); $i < $c; $i++) {
    if ($fieldNames[$i] == $newFieldName) {
      $existsFieldName = TRUE;
      break;
    }
  }
  if (!$existsFieldName) {
    for ($i = 0, $c = ptb_count($table); $i < $c; $i++) {
      $table[$i][$newFieldName] = $defaultValue;
    }
  }
  ptb_internal_write($filename, $location, '1'.PTB_DEFAULT_SECURITY.'1', $table);
}
// -----------------------------------------------------------[ ptb_delField ]--
function ptb_delField($filename, $location, $fieldName)
{
  $table = ptb_connect($filename, $location, FALSE);
  $fieldNames = array_keys($table[0]);
  $existsFieldName = FALSE;
  for ($i = 0, $c = ptb_count($fieldNames); $i < $c; $i++) {
    if ($fieldNames[$i] == $fieldName) {
      $existsFieldName = TRUE;
      break;
    }
  }
  if ($existsFieldName) {
    for ($i = 0, $c = ptb_count($table); $i < $c; $i++) {
      unset($table[$i][$fieldName]);
    }
    ptb_internal_write($filename, $location, '1'.PTB_DEFAULT_SECURITY.'1', $table);
  }
}
function isThere($needle, $haystack)
{
  if (!is_array($haystack)) {
    $haystack = explode(',', $haystack);
  }
  return in_array($needle, $haystack);
}
function isThereExt($haystack, $condition, $link = 'OR')
{
  $link = strtoupper($link);
  if (!is_array($haystack)) {
    $haystack = explode(',', $haystack);
  }
  if ($link == 'OR') {
    $is = FALSE;
  } else {
    $is = TRUE;
  }
  for ($i = 0, $c = count($haystack); $i < $c; $i++) {
    $piece = $haystack[$i];
    if ($link == 'OR') {
      eval("if ($condition) {\$is = TRUE;}");
      if ($is) {return $is;}
    } else {
      eval("if (!($condition)) {\$is = FALSE;}");
      if (!$is) {return $is;}
    }
  }
  return $is;
}
?>